package com.gin.no_gay_alliance.fragment.post.attach;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;

import com.gin.jackson.utils.ObjectUtils;
import com.gin.nga.callback.UploadCallback;
import com.gin.nga.client.NgaClient;
import com.gin.nga.exception.NgaClientException;
import com.gin.nga.exception.NgaException;
import com.gin.nga.exception.NgaServerException;
import com.gin.nga.method.NukeApi;
import com.gin.nga.params.UploadParam;
import com.gin.nga.response.body.UploadBody;
import com.gin.nga.response.field.Attachment;
import com.gin.nga.response.post.PrepareBody;
import com.gin.no_gay_alliance.R;
import com.gin.no_gay_alliance.adpter.AttachmentItemAdapter;
import com.gin.no_gay_alliance.adpter.base.BaseBindingRecyclerViewAdapter;
import com.gin.no_gay_alliance.app.MyApplication;
import com.gin.no_gay_alliance.databinding.FragmentUploadBinding;
import com.gin.no_gay_alliance.databinding.ItemUploadBodyBinding;
import com.gin.no_gay_alliance.exception.RequireParamException;
import com.gin.no_gay_alliance.fragment.base.BaseBindingFragment;
import com.gin.no_gay_alliance.itemdecoration.SimpleItemDecoration;
import com.gin.no_gay_alliance.utils.AlertDialogUtils;
import com.gin.no_gay_alliance.utils.HandlerMessage;
import com.gin.no_gay_alliance.utils.ViewUtils;
import com.gin.no_gay_alliance.viewmodel.FileInfo;
import com.gin.no_gay_alliance.viewmodel.InsertText;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import okhttp3.Call;

/**
 * 上传附件
 *
 * @author : ginstone
 * @version : v1.0.0
 * @since : 2023/8/29 09:22
 **/

public class UploadFragment extends BaseBindingFragment<FragmentUploadBinding> implements ActivityResultCallback<ActivityResult>, View.OnClickListener {
    public static final String TAG = UploadFragment.class.getSimpleName();
    /**
     * 允许上传的后缀名
     */
    private static final List<String> AVAILABLE_EXT = new ArrayList<>(Arrays.asList("jpg", "jpeg", "png", "gif", "mp3", "mp4", "zip"));
    private ActivityResultLauncher<Intent> launcher;
    /**
     * 上传的附件
     */
    private List<UploadBody> uploadBodyList;
    /**
     * 上传的文件信息
     */
    private List<FileInfo> fileInfoList;
    /**
     * 临时目录
     */
    private File attachDir;
    private BaseBindingRecyclerViewAdapter<ItemUploadBodyBinding, UploadBody> adapter;
    /**
     * 参数
     */
    private Args args;

    /**
     * 初始化
     */
    @Override
    protected void onCreateView() {
        if (getArguments() != null) {
            this.args = getArguments().getParcelable(Args.class.getName());
        } else {
            throw new RequireParamException();
        }
        // 创建暂存上传文件的目录
        attachDir = new File(requireActivity().getCacheDir(), "attachments");
        //noinspection ResultOfMethodCallIgnored
        attachDir.mkdirs();

        // 初始化上传列表
        fileInfoList = new ArrayList<>();
        fileInfoList.add(null);
        uploadBodyList = new ArrayList<>();
        uploadBodyList.add(null);
        // 适配器
        adapter = new BaseBindingRecyclerViewAdapter<ItemUploadBodyBinding, UploadBody>(requireActivity(), uploadBodyList) {
            @Override
            protected void onBindViewHolder(ViewHolder<ItemUploadBodyBinding> holder, @NonNull ItemUploadBodyBinding b, int position, UploadBody item) {
                ImageView iv = b.iv;
                TextView tv = b.tv;
                ViewUtils.setOnClickListener(UploadFragment.this, iv, tv);
                if (position == 0) {
                    iv.setImageResource(R.drawable.ic_upload);
                    iv.setBackgroundResource(R.drawable.shape_upload_background);
                    tv.setText("上传");
                    ViewUtils.setTag(null, iv, tv);
                } else {
                    iv.setBackground(null);
                    tv.setText("删除");
                    ViewUtils.setTag(item, iv, tv);


                    FileInfo fileInfo = fileInfoList.get(position);
                    // 根据文件类型选择图标显示内容
                    AttachmentItemAdapter.configAttachmentThumb(fileInfo.ext, iv,
                            i -> i.setImageURI(fileInfo.uri));
                }
            }

            @NonNull
            @Override
            protected ItemUploadBodyBinding buildBinding(LayoutInflater inflater) {
                return ItemUploadBodyBinding.inflate(inflater);
            }
        };
        b.recyclerView.setAdapter(adapter);
        b.recyclerView.addItemDecoration(new SimpleItemDecoration());

        launcher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this);
    }


    /**
     * grid中的item的点击事件
     *
     * @param v The view that was clicked.
     */
    @Override
    public void onClick(View v) {
        UploadBody body = (UploadBody) v.getTag();
        // 上传请求
        if (body == null) {
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_GET_CONTENT);
            intent.setType("*/*");
            try {
                launcher.launch(intent);
            } catch (ActivityNotFoundException e) {
                getParentHandler().toast("没有匹配的文件选择器");
            }
        } else {
            if (v.getId() == R.id.tv) {
                // 删除
                AlertDialogUtils.showConfirm(requireContext(), "移除附件", "即将移除上传的附件", (dialog, which) -> {
                    if (which == DialogInterface.BUTTON_POSITIVE) {
                        int index = uploadBodyList.indexOf(body);
                        uploadBodyList.remove(index);
                        fileInfoList.remove(index);
                        adapter.notifyItemRemoved(index);
                        // 同步附件列表到activity
                        syncBodyToActivity();
                    }
                });
            }
            if (v.getId() == R.id.iv) {
                // 插入代码

                // 图片
                if (body.getIsImg() != null && body.getIsImg()) {
                    String url;
                    if (!ObjectUtils.isEmpty(body.getThumbUrls())) {
                        // 优先选择缩略图
                        url = body.getThumbUrls().get(body.getThumbUrls().size() - 1);
                    } else {
                        // 原图
                        url = body.getUrl();
                    }
                    String code = String.format("[img]./%s[/img]", url);
                    sendInsertMessage(code);
                } else if ("zip".equals(body.getExtension())) {
                    // 压缩包
                    int index = uploadBodyList.indexOf(body);
                    FileInfo fileInfo = fileInfoList.get(index);
                    String code = String.format("[url=%s%s]下载文件: %s[/url]"
                            , Attachment.PREFIX
                            , body.getUrl()
                            , fileInfo.originalName
                    );
                    sendInsertMessage(code);
                } else if ("mp3".equals(body.getExtension()) || "mp4".equals(body.getExtension())) {
                    // 音视频
                    sendInsertMessage(body.getBbsCode());
                }

                // todo 长按预览或其他操作


            }
        }
    }

    /**
     * 选中文件后的处理
     *
     * @param result
     */
    @Override
    public void onActivityResult(ActivityResult result) {
        // 选择附件回调

        if (result.getResultCode() == Activity.RESULT_OK) {
            Intent intent = result.getData();
            if (intent != null) {
                Uri uri = intent.getData();
                if (uri != null) {
                    ContentResolver cr = requireActivity().getContentResolver();
                    // 读取文件信息
                    FileInfo fileInfo = new FileInfo();
                    fileInfo.read(cr, uri);



                    if (AVAILABLE_EXT.stream().noneMatch(ext->ext.equalsIgnoreCase(fileInfo.ext))) {
                        toastLong("仅支持上传如下类型文件: " + String.join(",", AVAILABLE_EXT));
                        return;
                    }

                    // 复制文件到临时目录

                    // 文件名
                    String filename = (fileInfo.originalName != null ? fileInfo.originalName : (System.currentTimeMillis()) + "." + fileInfo.ext);
                    File destFile = new File(attachDir, filename);

                    // 复制文件是否成功
                    boolean success = copyFile(uri, cr, destFile);
                    if (!success) {
                        toast("文件复制失败: " + destFile.getName());
                        return;
                    }
                    // 上传文件
                    NgaClient client = MyApplication.getClient();
                    // 参数
                    UploadParam param = new UploadParam(destFile, args.auth, args.forumId);
                    // 请求
                    toast("正在上传...");
                    NukeApi.attachUpload(client, args.attachUrl, param).async(new UploadCallback() {
                        @Override
                        public void onFailure(@NonNull Call call, @NonNull IOException e) {
                            super.onFailure(call, e);
                            toast("上传失败, 请重试");
                            if (destFile.delete()) {
                                Log.d(TAG, "删除临时文件: " + destFile.getName());
                            }
                        }

                        @Override
                        public void onSuccess(UploadBody body) {
                            // 上传成功
                            // 删除临时文件
                            if (destFile.delete()) {
                                Log.d(TAG, "删除临时文件: " + destFile.getName());
                            }
                            // 保存文件信息
                            fileInfoList.add(fileInfo);
                            // 添加body到列表
                            uploadBodyList.add(body);
                            //  同步body列表到activity
                            syncBodyToActivity();
                            toast("上传成功");
                            // 通知adapter更新
                            getParentHandler().post(() -> adapter.notifyItemInserted(uploadBodyList.size() - 1));
                        }

                        @Override
                        public void handleException(NgaException e) {
                            super.handleException(e);
                            if (e instanceof NgaClientException) {
                                String message = ((NgaClientException) e).getReason().get(0);
                                toast(message);
                            }
                            if (e instanceof NgaServerException) {
                                toast("上传失败, 文件过大或图片尺寸过大");
                                if (destFile.delete()) {
                                    Log.d(TAG, "删除临时文件: " + destFile.getName());
                                }
                            }
                        }
                    });

                } else {
                    toast("未选择附件");
                }
            }
        }
    }

    /**
     * 从uri把文件复制到临时目录
     *
     * @param uri      uri
     * @param cr       ContentResolver
     * @param destFile 目标文件
     * @return 是否成功;
     */
    private boolean copyFile(Uri uri, ContentResolver cr, File destFile) {
        try (BufferedInputStream bis = new BufferedInputStream(cr.openInputStream(uri))) {
            if (!destFile.createNewFile()) {
                getParentHandler().toast("文件创建失败: " + destFile.getName());
                return false;
            }
            BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(destFile.toPath()));
            int bytesRead;
            byte[] buffer = new byte[1024];
            while ((bytesRead = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
            }
            bos.flush();
            bos.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 发送插入消息
     */
    private void sendInsertMessage(String start) {
        getParentHandler().sendMessage(getParentHandler().obtainMessage(HandlerMessage.INSERT_TEXT, new InsertText(start, null)));
    }

    /**
     * 同步附件列表到activity
     */
    private void syncBodyToActivity() {
        List<UploadBody> list = (uploadBodyList != null && uploadBodyList.size() > 1) ? uploadBodyList.subList(1, uploadBodyList.size()) : new ArrayList<>();
        getParentHandler().sendMessage(getParentHandler().obtainMessage(HandlerMessage.UPDATE_UPLOAD_BODY, list));
    }

    public static UploadFragment newInstance(Args args) {
        UploadFragment fragment = new UploadFragment();
        Bundle bundle = new Bundle();
        bundle.putParcelable(Args.class.getName(), args);
        fragment.setArguments(bundle);
        return fragment;
    }

    public static class Args implements Parcelable {
        public static final Creator<Args> CREATOR = new Creator<Args>() {
            @Override
            public Args createFromParcel(Parcel in) {
                return new Args(in);
            }

            @Override
            public Args[] newArray(int size) {
                return new Args[size];
            }
        };
        public String auth;
        public String attachUrl;
        public Long forumId;

        public Args(String auth, String attachUrl, Long forumId) {
            this.auth = auth;
            this.attachUrl = attachUrl;
            this.forumId = forumId;
        }

        public Args(PrepareBody body) {
            this.auth = body.getAuth();
            this.attachUrl = body.getAttachUrl();
            this.forumId = body.getForumId();
        }

        protected Args(Parcel in) {
            auth = in.readString();
            attachUrl = in.readString();
            if (in.readByte() == 0) {
                forumId = null;
            } else {
                forumId = in.readLong();
            }
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(auth);
            dest.writeString(attachUrl);
            if (forumId == null) {
                dest.writeByte((byte) 0);
            } else {
                dest.writeByte((byte) 1);
                dest.writeLong(forumId);
            }
        }
    }
}